#ifndef TB_CORE_LOGGING_HPP__
#define TB_CORE_LOGGING_HPP__

#include <ostream>

#include "base_export.hpp"
#include "base_fwd.hpp"
#include "basic_types.hpp"
#include "string.hpp"
#include "base.hpp"

TB_NAMESPACE_BEGIN

enum LogSeverity {
  kLogVerbose,
  kLogKernel,
  kLogInfo,
  kLogError,
  kLogWarning,
  kLogFatal,
  kLogDebug,
  kLogCustom,
};

enum LogLockState { kLogLockFile, kLogDontLockFile };
enum LogFileState { kLogDeleteExistFile, kLogAppendToExistFile }; 

MSVC_PUSH_DISABLE_WARNING(4251)

struct TB_BASE_API LoggingSettings {
  LoggingSettings();
  std::streamsize logPrecision;
  bool logTimestamp;
  bool logProcessId;
  bool logThreadId;
  bool logFileAndLine;
  String logFile;
  LogLockState lockState;
  LogFileState fileState;
};

MSVC_POP_WARNING()

struct AsyncLoggerInterface {
  virtual void AsyncAppendLog(const String &msg) = 0;
};

class TB_BASE_API LogMessage {
 public:
	LogMessage(const TCHAR*, int, LogSeverity);
  ~LogMessage();
  // This has to be an operator with a precedence lower than << but
  // higher than ?:
  void operator&(std::ostream &);
  void operator&(WOStream &);
 private:
   struct LogMessageImpl;
   LogMessageImpl* impl_;
};

TB_BASE_API void AppendLog(LogSeverity severity, const char *msg);
TB_BASE_API void SetAsyncLogger(const AsyncLoggerInterfaceWPtr &loggingProxy);
TB_BASE_API void InitLoggingSettings(const LoggingSettings &settings = LoggingSettings());
TB_BASE_API void CloseLogFile();

TB_NAMESPACE_END

#define TB_LAZY_STREAM(CONDITION, LOGGER, STREAM)                                  \
  !(CONDITION) ? (void) 0 : LOGGER & (STREAM)

#define TB_IMPL_SERVERITY_LOGGER(SERVERITY) \
  TB_LAZY_STREAM(true, TB_NAMESPACE::LogMessage(_T(__FILE__), __LINE__, SERVERITY), TB_NAMESPACE::YSStringStream())

#define __TB_IMPL_SERVERITY_LOGGER(SERVERITY) \
  TB_LAZY_STREAM(true, TB_NAMESPACE::LogMessage(_T(__FILE__), __LINE__, SERVERITY), std::stringstream())

#define LKERNEL() \
  TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogKernel)

#define _LKERNEL() \
	__TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogKernel)

#define LERROR() \
  TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogError)

#define _LERROR() \
	__TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogError)

#define LWARNING() \
  TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogWarning)

#define _LWARNING() \
	__TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogWarning)

#define LINFO() \
  TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogInfo)

#define _LINFO() \
	__TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogInfo)

#define LFATAL() \
  TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogFatal)

#define _LFATAL() \
	__TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogFatal)

#define LDEBUG() \
  TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogDebug)

#define _LDEBUG() \
	__TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogDebug)

#define LMETHOD(m) \
	TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogDebug) << _T("method["#m"]:")

#define _LMETHOD(m) \
	__TB_IMPL_SERVERITY_LOGGER(TB_NAMESPACE::kLogDebug) << _T("method["#m"]:")

#define NOTREACHED() \
  LERROR() << _T("NOTREACHED hit in ")

#define _NOTREACHED() \
	_LERROR() << "NOTREACHED hit in "

#define TB_IMPL_SERVERITY_COND_LOGGER(CONDITION, SERVERITY) \
  TB_LAZY_STREAM(CONDITION, TB_NAMESPACE::LogMessage(_T(__FILE__), __LINE__, SERVERITY), TB_NAMESPACE::YSStringStream())

#define __TB_IMPL_SERVERITY_COND_LOGGER(CONDITION, SERVERITY) \
  TB_LAZY_STREAM(CONDITION, TB_NAMESPACE::LogMessage(_T(__FILE__), __LINE__, SERVERITY), std::stringstream())

#define DCHECK(cond) \
	TB_IMPL_SERVERITY_COND_LOGGER(cond, TB_NAMESPACE::kLogDebug)

#define _DCHECK(cond) \
	__TB_IMPL_SERVERITY_COND_LOGGER(cond, TB_NAMESPACE::kLogDebug)

#define DCHECK_EQ(l, r) \
  TB_IMPL_SERVERITY_COND_LOGGER((l == r), TB_NAMESPACE::kLogDebug)

#define _DCHECK_EQ(l, r) \
	__TB_IMPL_SERVERITY_COND_LOGGER((l == r), TB_NAMESPACE::kLogDebug)

#define DCHECK_LE(l, r) \
  TB_IMPL_SERVERITY_COND_LOGGER((l <= r), TB_NAMESPACE::kLogDebug)

#define _DCHECK_LE(l, r) \
	__TB_IMPL_SERVERITY_COND_LOGGER((l <= r), TB_NAMESPACE::kLogDebug)

#define DCHECK_GT(l, r) \
	TB_IMPL_SERVERITY_COND_LOGGER((l >= r), TB_NAMESPACE::kLogDebug)

#define _DCHECK_GT(l, r) \
	__TB_IMPL_SERVERITY_COND_LOGGER((l >= r), TB_NAMESPACE::kLogDebug)

#endif // TB_CORE_LOGGING_HPP__
